/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.fei.ClassDiagram.drawing.Classes;

import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import javax.swing.JCheckBox;
import org.fei.ClassDiagram.data.MethodLook;
import org.fei.ClassDiagram.data.VariableLook;
import org.fei.ClassDiagram.WindowComponents.Message;
import org.fei.ClassDiagram.drawing.Point;
import org.netbeans.modules.classfile.ClassFile;

/**
 *
 * @author Tomas
 */
abstract public class Drawing {
    
    protected ArrayList<MethodLook> methods;
    protected ArrayList<VariableLook> variables;
    protected ClassFile classFile;
    protected Point topPoint;
    protected Point bottomPoint;
    protected Point rightPoint;
    protected Point leftPoint;
    protected Point innerClassesPoint;
    
    protected int cornerX;
    protected int cornerY;
    protected int width;
    protected int height;
    protected int labelPosition;
    protected int namePosition;
    protected int varEnd;
    protected int nameEnd;
    
    protected static int fontSize = 12;
    protected boolean isSuperclass = false;
    protected boolean isAsotiationClass = false;
    protected boolean hasInnerClasses = false;

    private boolean isEnum;
    private boolean isInterface;
    //private String className;
    // premenna zavedena koli asociaciam, pocita kolko sa ich vztahuje k danej triede
    // na zaklade toho ich mozem vykreslovat pod sebou
    private int numberOfAsotiation = 0;
    
    // pomocne premenne pre h-layout
    private int level;
    private int position;
    
    // premenne pre checkboxy
    private static boolean showAttributes = false;
    private static boolean showConstructors = false;
    
    // ak je trieda genericka zobrazi sa k nazvu aj vycet parametrov
    protected String generic = new String();       

    public Drawing(int cornerX, int cornerY, int width, int height, ClassFile classFile, ArrayList<MethodLook> methodLook, ArrayList<VariableLook> variableLook, boolean isEnum, boolean isInterface) {
        this.cornerX = cornerX;
        this.cornerY = cornerY;
        this.width = width;
        this.height = height;
        //this.fontSize = 12;
        topPoint = new Point();
        bottomPoint = new Point();
        rightPoint = new Point();
        leftPoint = new Point();
        innerClassesPoint = new Point();
        this.methods = methodLook;
        this.variables = variableLook;
        this.classFile = classFile;
        
        this.isEnum = isEnum;
        this.isInterface = isInterface;
        
        this.parseGeneric();
        //Message.showMessage(Parser.parseClassName(classFile.getName().toString(), source));
    }
    
    
    public void draw(Graphics g) {
        Graphics2D g2d = (Graphics2D)g;
        // The rendering hints are used to make the drawing smooth.
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);       
        g2d.setRenderingHints(rh);
        
        //Font f = this.setClassNameFont();
        FontMetrics fm = g.getFontMetrics();
        // zistenie max sirky
        int maxWidth = this.getClassNameWidth(g);
        int actualHeight = 18;
        
        actualHeight = this.setLabelNamePosition(actualHeight, fm);
        
        for (VariableLook v : variables) {
            if (!v.isIsAsotiation() || Drawing.showAttributes) {
                v.draw(g2d, cornerX + 5, cornerY + actualHeight, isEnum, fontSize);
                maxWidth = (maxWidth < v.getStringWidth()) ? v.getStringWidth() : maxWidth;
                actualHeight += v.getStringHeight();
            }
        }
        
        actualHeight -= fm.getAscent()/2;
        varEnd = actualHeight;
        actualHeight += 14;
        
        for (MethodLook m : methods) {
            if (!(m.isConstructor() && !Drawing.showConstructors)) {
                m.draw(g2d, cornerX + 5, cornerY + actualHeight, isInterface, fontSize);
                maxWidth = (maxWidth < m.getStringWidth()) ? m.getStringWidth() : maxWidth;
                actualHeight += m.getStringHeight();
            }
        }
        
        actualHeight -= fm.getAscent()/2;
        // nastavenie sirky vysky obdlznika
        width = maxWidth + 10;
        height = actualHeight;
        // nastavenie bodov na spajanie tried
        this.topPoint.setX(cornerX + width/2);
        this.topPoint.setY(cornerY);
        this.bottomPoint.setX(topPoint.getX());
        this.bottomPoint.setY(cornerY + height);
        this.rightPoint.setX(cornerX + width);
        this.rightPoint.setY(cornerY + height/2);
        this.innerClassesPoint.setX(cornerX + width);
        if (this.isAsotiationClass) // ak sa uz vykresluje asociacia vykresli sa vnorena trieda vyssie, inak sa vykresli vstrede
            this.innerClassesPoint.setY(cornerY + height/2 - height/4);
        else
            this.innerClassesPoint.setY(cornerY + height/2);
        this.leftPoint.setX(cornerX);
        this.leftPoint.setY(rightPoint.getY());
        // ak je rodicovska trieda vykresli sa sipocka
        if (this.isSuperclass)
            this.drawInheritanceArrow(g2d);
        
        // ak je tato trieda vo vztahu asociacie vykresli sa sipka
        if (this.isAsotiationClass)
            this.drawLeftAnotationArrow(g2d);
        
        // ak tato trieda ma vnorene triedy vykresli sa sipka
        if (this.hasInnerClasses)
            this.drawLeftInnerClassNest(g2d);
        
        // obdlznik triedy
        g2d.drawRect(cornerX, cornerY, width, height);
        this.drawLabel(g2d);
        // meno triedy v strede
        Font f = this.setClassNameFont();
        fm = g.getFontMetrics(f);
        g2d.setFont(f);
        g2d.drawString(classFile.getName().toString() + generic/* + " (" + level + ", " + position + ")"*/, cornerX + width/2 - (fm.stringWidth(classFile.getName().toString() + generic)/2), cornerY+namePosition);
        // oddelenie mena
        g2d.drawLine(cornerX, cornerY + nameEnd, cornerX + width, cornerY + nameEnd);
        // oddelenie atributov od metod
        g2d.drawLine(cornerX, cornerY + varEnd, cornerX + width, cornerY + varEnd);
    }
    
    abstract protected int getClassNameWidth(Graphics g);
    abstract protected Font setClassNameFont();
    abstract protected int setLabelNamePosition(int actualHeight, FontMetrics fm);
    protected void drawLabel(Graphics2D g2d) {}
    
    private void parseGeneric() {
        if (classFile.getTypeSignature() == null)
            return;
        //Message.showMessage(classFile.getTypeSignature());
        String typeSignature = classFile.getTypeSignature();
        String type = new String();
        ArrayList<String> types = new ArrayList<String>();
        boolean start = false;
        
        for (int i = 0; i < typeSignature.length(); i++) {
            // tu genericky typ konci
            if (typeSignature.charAt(i) == ':') {
                start = false;
                types.add(type);
                type = null;
                type = new String();
                continue;
            }
            // tu zacina
            if (typeSignature.charAt(i) == '<') {
                start = true;
                continue;
            }
            if (typeSignature.charAt(i) == ';') {
                start = true;
                continue;
            }
            // tu sa zapise
            if (start == true) {
                type += typeSignature.charAt(i);
            }           
        }
        //Message.showMessage(types.toString());
        
        if (types.size() > 0) {
            generic += " <";
            for (int i = 0; i < types.size(); i++) {
                generic += types.get(i);
                if (i + 1 == types.size()) {
                    generic += ">";
                }
                else {
                    generic += ", ";
                }
            }
        }
    }
    
    public boolean isOver(int x, int y) {       
        if (x > cornerX + width)
            return false;
        if (x < cornerX)
            return false;
        if (y > cornerY + height)
            return false;
        if (y < cornerY)
            return false;
        return true; 
    }
    
    protected void drawInheritanceArrow(Graphics2D g) {
        int x = bottomPoint.getX();
        int y = bottomPoint.getY();
        g.drawLine(x, y, x - 8, y + 10);
        g.drawLine(x, y, x + 8, y + 10);
        g.drawLine(x + 8, y + 10, x - 8, y + 10);
        g.drawLine(x, y + 10, x, y + 20);
        bottomPoint.setY(y + 20);
    }
    
    protected void drawLeftAnotationArrow(Graphics2D g) {
        int x = rightPoint.getX();
        int y = rightPoint.getY();
        g.drawLine(x, y, x + 20, y);
        g.drawLine(x, y, x + 12, y - 6);
        g.drawLine(x, y, x + 12, y + 6);
        rightPoint.setX(x + 20);
    }
    
    protected void drawLeftInnerClassNest(Graphics2D g) {
        int x = innerClassesPoint.getX();
        int y = innerClassesPoint.getY();
        g.drawLine(x, y, x + 20, y);
        g.drawLine(x + 6, y, x + 6, y - 6);
        g.drawLine(x + 6, y, x + 6, y + 6);
        g.drawOval(x, y - 6, 12, 12);
        innerClassesPoint.setX(x + 20);
    }

    public void setCornerX(int cornerX) {
        this.cornerX = cornerX;
    }

    public void setCornerY(int cornerY) {
        this.cornerY = cornerY;
    }

    public int getCornerX() {
        return cornerX;
    }

    public int getCornerY() {
        return cornerY;
    }

    public static int getFontSize() {
        return fontSize;
    }

    public static void setFontSize(int fs) {
        fontSize = fs;
    }
    
    public ClassFile getClassFile() {
        return classFile;
    }

    public Point getBottomPoint() {
        return bottomPoint;
    }

    public Point getTopPoint() {
        return topPoint;
    }

    public Point getLeftPoint() {
        return leftPoint;
    }

    public Point getRightPoint() {
        return rightPoint;
    }

    public Point getInnerClassesPoint() {
        return innerClassesPoint;
    }

    public void setIsSuperclass(boolean isSuperclass) {
        this.isSuperclass = isSuperclass;
    }

    public String getGeneric() {
        return generic;
    }

    public void setGeneric(String generic) {
        this.generic = generic;
    }

    public void setIsAsotiationClass(boolean isAsotiationClass) {
        this.isAsotiationClass = isAsotiationClass;
    }

    public void setHasInnerClasses(boolean hasInnerClasses) {
        this.hasInnerClasses = hasInnerClasses;
    }

    public int getNumberOfAsotiation() {
        return numberOfAsotiation;
    }

    public void setNumberOfAsotiation(int numberOfAsotiation) {
        this.numberOfAsotiation = numberOfAsotiation;
    }

    public ArrayList<VariableLook> getVariables() {
        return variables;
    }
    
    public static void setShowAttributes(boolean a) {
        Drawing.showAttributes = a;
    }

    public static void setShowConstructors(boolean showConstructors) {
        Drawing.showConstructors = showConstructors;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public void setPosition(int position) {
        this.position = position;
    }

    public int getPosition() {
        return position;
    }

    public int getHeight() {
        return height;
    }

    public int getWidth() {
        return width;
    }
}
